home *** CD-ROM | disk | FTP | other *** search
/ 130 MIDI Tool Box / 130 MIDI Tool Box.iso / kbmidi / kbmidi.c < prev    next >
C/C++ Source or Header  |  1992-01-27  |  29KB  |  777 lines

  1. /*---------------------------------------------------------
  2.    KBMIDI.C -- Keyboard MIDI Player for Multimedia Windows
  3.                (c) Charles Petzold, 1992
  4.   ---------------------------------------------------------*/
  5.  
  6.  
  7. #include <windows.h>
  8. extern "C" {
  9. #include <mmsystem.h>
  10. }
  11. #include <string.h>
  12. typedef unsigned int UINT ;
  13.  
  14. #define min(a,b) (((a) < (b)) ? (a) : (b))
  15. #define max(a,b) (((a) > (b)) ? (a) : (b))
  16.  
  17.           // Defines for Menu IDs
  18.           // --------------------
  19.  
  20. #define IDM_OPEN    0x100
  21. #define IDM_CLOSE   0x101
  22. #define IDM_DEVICE  0x200
  23. #define IDM_CHANNEL 0x300
  24. #define IDM_VOICE   0x400
  25.  
  26.           // Forward declaration of WndProc
  27.           // ------------------------------
  28.  
  29. long FAR PASCAL _export WndProc (HWND, UINT, UINT, LONG);
  30.  
  31.           // Global variables
  32.           // ----------------
  33.  
  34. char     szAppName [] = "KBMidi" ;
  35. HMIDIOUT hMidiOut ;
  36. int      iDevice = MIDIMAPPER, iChannel = 0, iVoice = 0, iVelocity = 64 ;
  37. int      cxCaps, cyChar, xOffset, yOffset ;
  38.  
  39.           // Structures for family and instrument names
  40.           // ------------------------------------------
  41.  
  42. typedef struct
  43.      {
  44.      char * szInst ;
  45.      int    iVoice ;
  46.      }
  47.      INSTRUMENT ;
  48.  
  49. typedef struct
  50.      {
  51.      char       * szFam ;
  52.      INSTRUMENT   inst [8] ;
  53.      }
  54.      FAMILY ;
  55.  
  56. FAMILY fam [16] = { "Piano",
  57.  
  58.                     "Acoustic Grand Piano",        0,
  59.                     "Bright Acoustic Piano",       1,
  60.                     "Electric Grand Piano",        2,
  61.                     "Honky-tonk Piano",            3,
  62.                     "Rhodes Piano",                4,
  63.                     "Chorused Piano",              5,
  64.                     "Harpsichord",                 6,
  65.                     "Clavinet",                    7,
  66.  
  67.                     "Chromatic Percussion",
  68.  
  69.                     "Celesta",                     8,
  70.                     "Glockenspiel",                9,
  71.                     "Music box",                   10,
  72.                     "Vibraphone",                  11,
  73.                     "Marimba",                     12,
  74.                     "Xylophone",                   13,
  75.                     "Tubular Bells",               14,
  76.                     "Dulcimer",                    15,
  77.  
  78.                     "Organ",
  79.  
  80.                     "Hammond Organ",               16,
  81.                     "Percussive Organ",            17,
  82.                     "Rock Organ",                  18,
  83.                     "Church Organ",                19,
  84.                     "Reed Organ",                  20,
  85.                     "Accordian",                   21,
  86.                     "Harmonica",                   22,
  87.                     "Tango Accordian",             23,
  88.  
  89.                     "Guitar",
  90.  
  91.                     "Acoustic Guitar (nylon)",     24,
  92.                     "Acoustic Guitar (steel)",     25,
  93.                     "Electric Guitar (jazz)",      26,
  94.                     "Electric Guitar (clean)",     27,
  95.                     "Electric Guitar (muted)",     28,
  96.                     "Overdriven Guitar",           29,
  97.                     "Distortion Guitar",           30,
  98.                     "Guitar Harmonics",            31,
  99.  
  100.                     "Bass",
  101.  
  102.                     "Acoustic Bass",               32,
  103.                     "Electric Bass (finger)",      33,
  104.                     "Electric Bass (pick)",        34,
  105.                     "Fretless Bass",               35,
  106.                     "Slap Bass 1",                 36,
  107.                     "Slap Bass 2",                 37,
  108.                     "Synth Bass 1",                38,
  109.                     "Synth Bass 2",                39,
  110.  
  111.                     "Strings",
  112.  
  113.                     "Violin",                      40,
  114.                     "Viola",                       41,
  115.                     "Cello",                       42,
  116.                     "Contrabass",                  43,
  117.                     "Tremolo Strings",             44,
  118.                     "Pizzicato Strings",           45,
  119.                     "Orchestral Harp",             46,
  120.                     "Timpani",                     47,
  121.  
  122.                     "Ensemble",
  123.  
  124.                     "String Ensemble 1",           48,
  125.                     "String Ensemble 2",           49,
  126.                     "Synth Strings 1",             50,
  127.                     "Synth Strings 2",             51,
  128.                     "Choir Aahs",                  52,
  129.                     "Voice Oohs",                  53,
  130.                     "Synth Voice",                 54,
  131.                     "Orchestra Hit",               55,
  132.  
  133.                     "Brass",
  134.  
  135.                     "Trumpet",                     56,
  136.                     "Trombone",                    57,
  137.                     "Tuba",                        58,
  138.                     "Muted Trumpet",               59,
  139.                     "French Horn",                 60,
  140.                     "Brass Section",               61,
  141.                     "Synth Brass 1",               62,
  142.                     "Synth Brass 2",               63,
  143.  
  144.                     "Reed",
  145.  
  146.                     "Soprano Sax",                 64,
  147.                     "Alto Sax",                    65,
  148.                     "Tenor Sax",                   66,
  149.                     "Baritone Sax",                67,
  150.                     "Oboe",                        68,
  151.                     "English Horn",                69,
  152.                     "Bassoon",                     70,
  153.                     "Clarinet",                    71,
  154.  
  155.                     "Pipe",
  156.  
  157.                     "Piccolo",                     72,
  158.                     "Flute",                       73,
  159.                     "Recorder",                    74,
  160.                     "Pan Flute",                   75,
  161.                     "Bottle Blow",                 76,
  162.                     "Shakuhachi",                  77,
  163.                     "Whistle",                     78,
  164.                     "Ocarina",                     79,
  165.  
  166.                     "Synth Lead",
  167.  
  168.                     "Lead 1 (square)",             80,
  169.                     "Lead 2 (sawtooth)",           81,
  170.                     "Lead 3 (caliope lead)",       82,
  171.                     "Lead 4 (chiff lead)",         83,
  172.                     "Lead 5 (charang)",            84,
  173.                     "Lead 6 (voice)",              85,
  174.                     "Lead 7 (fifths)",             86,
  175.                     "Lead 8 (brass + lead)",       87,
  176.  
  177.                     "Synth Pad",
  178.  
  179.                     "Pad 1 (new age)",             88,
  180.                     "Pad 2 (warm)",                89,
  181.                     "Pad 3 (polysynth)",           90,
  182.                     "Pad 4 (choir)",               91,
  183.                     "Pad 5 (bowed)",               92,
  184.                     "Pad 6 (metallic)",            93,
  185.                     "Pad 7 (halo)",                94,
  186.                     "Pad 8 (sweep)",               95,
  187.  
  188.                     "Synth Effects",
  189.  
  190.                     "FX 1 (rain)",                 96,
  191.                     "FX 2 (soundtrack)",           97,
  192.                     "FX 3 (crystal)",              98,
  193.                     "FX 4 (atmosphere)",           99,
  194.                     "FX 5 (brightness)",           100,
  195.                     "FX 6 (goblins)",              101,
  196.                     "FX 7 (echoes)",               102,
  197.                     "FX 8 (sci-fi)",               103,
  198.  
  199.                     "Ethnic",
  200.  
  201.                     "Sitar",                       104,
  202.                     "Banjo",                       105,
  203.                     "Shamisen",                    106,
  204.                     "Koto",                        107,
  205.                     "Kalimba",                     108,
  206.                     "Bagpipe",                     109,
  207.                     "Fiddle",                      110,
  208.                     "Shanai",                      111,
  209.  
  210.                     "Percussive",
  211.  
  212.                     "Tinkle Bell",                 112,
  213.                     "Agogo",                       113,
  214.                     "Steel Drums",                 114,
  215.                     "Woodblock",                   115,
  216.                     "Taiko Drum",                  116,
  217.                     "Melodic Tom",                 117,
  218.                     "Synth Drum",                  118,
  219.                     "Reverse Cymbal",              119,
  220.  
  221.                     "Sound Effects",
  222.  
  223.                     "Guitar Fret Noise",           120,
  224.                     "Breath Noise",                121,
  225.                     "Seashore",                    122,
  226.                     "Bird Tweet",                  123,
  227.                     "Telephone Ring",              124,
  228.                     "Helicopter",                  125,
  229.                     "Applause",                    126,
  230.                     "Gunshot",                     127 } ;
  231.  
  232.           // Data for translating scan codes to octaves and notes
  233.           // ----------------------------------------------------
  234.  
  235. #define NUMSCANS    (sizeof key / sizeof key[0])
  236.  
  237. struct
  238.      {
  239.      int    iOctave ;
  240.      int    iNote ;
  241.      int    yPos ;
  242.      int    xPos ;
  243.      char * szKey ;
  244.      }
  245.      key [] =
  246.      {
  247.                               // Scan  Char  Oct  Note
  248.                               // ----  ----  ---  ----
  249.      -1, -1, -1, -1, NULL,    //   0   None
  250.      -1, -1, -1, -1, NULL,    //   1   Esc
  251.      -1, -1,  0,  0, "",      //   2     1
  252.       5,  1,  0,  2, "C#",    //   3     2    5    C#
  253.       5,  3,  0,  4, "D#",    //   4     3    5    D#
  254.      -1, -1,  0,  6, "",      //   5     4
  255.       5,  6,  0,  8, "F#",    //   6     5    5    F#
  256.       5,  8,  0, 10, "G#",    //   7     6    5    G#
  257.       5, 10,  0, 12, "A#",    //   8     7    5    A#
  258.      -1, -1,  0, 14, "",      //   9     8
  259.       6,  1,  0, 16, "C#",    //  10     9    6    C#
  260.       6,  3,  0, 18, "D#",    //  11     0    6    D#
  261.      -1, -1,  0, 20, "",      //  12     -
  262.       6,  6,  0, 22, "F#",    //  13     =    6    F#
  263.      -1, -1, -1, -1, NULL,    //  14    Back
  264.  
  265.      -1, -1, -1, -1, NULL,    //  15    Tab
  266.       5,  0,  1,  1, "C",     //  16     q    5    C
  267.       5,  2,  1,  3, "D",     //  17     w    5    D
  268.       5,  4,  1,  5, "E",     //  18     e    5    E
  269.       5,  5,  1,  7, "F",     //  19     r    5    F
  270.       5,  7,  1,  9, "G",     //  20     t    5    G
  271.       5,  9,  1, 11, "A",     //  21     y    5    A
  272.       5, 11,  1, 13, "B",     //  22     u    5    B
  273.       6,  0,  1, 15, "C",     //  23     i    6    C
  274.       6,  2,  1, 17, "D",     //  24     o    6    D
  275.       6,  4,  1, 19, "E",     //  25     p    6    E
  276.       6,  5,  1, 21, "F",     //  26     [    6    F
  277.       6,  7,  1, 23, "G",     //  27     ]    6    G
  278.      -1, -1, -1, -1, NULL,    //  28    Ent
  279.  
  280.      -1, -1, -1, -1, NULL,    //  29    Ctrl
  281.       3,  8,  2,  2, "G#",    //  30     a    3    G#
  282.       3, 10,  2,  4, "A#",    //  31     s    3    A#
  283.      -1, -1,  2,  6, "",      //  32     d
  284.       4,  1,  2,  8, "C#",    //  33     f    4    C#
  285.       4,  3,  2, 10, "D#",    //  34     g    4    D#
  286.      -1, -1,  2, 12, "",      //  35     h
  287.       4,  6,  2, 14, "F#",    //  36     j    4    F#
  288.       4,  8,  2, 16, "G#",    //  37     k    4    G#
  289.       4, 10,  2, 18, "A#",    //  38     l    4    A#
  290.      -1, -1,  2, 20, "",      //  39     ;
  291.       5,  1,  2, 22, "C#",    //  40     '    5    C#
  292.      -1, -1, -1, -1, NULL,    //  41     `
  293.  
  294.      -1, -1, -1, -1, NULL,    //  42    Shift
  295.      -1, -1, -1, -1, NULL,    //  43     \           (not line continuation)
  296.       3,  9,  3,  3, "A",     //  44     z    3    A
  297.       3, 11,  3,  5, "B",     //  45     x    3    B
  298.       4,  0,  3,  7, "C",     //  46     c    4    C
  299.       4,  2,  3,  9, "D",     //  47     v    4    D
  300.       4,  4,  3, 11, "E",     //  48     b    4    E
  301.       4,  5,  3, 13, "F",     //  49     n    4    F
  302.       4,  7,  3, 15, "G",     //  50     m    4    G
  303.       4,  9,  3, 17, "A",     //  51     ,    4    A
  304.       4, 11,  3, 19, "B",     //  52     .    4    B
  305.       5,  0,  3, 21, "C"      //  53     /    5    C
  306.      } ;
  307.  
  308.           // Standard WinMain
  309.           // ----------------
  310.  
  311. int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
  312.             LPSTR lpszCmdLine, int nCmdShow)
  313.      {
  314.      MSG      msg;
  315.      HWND     hwnd ;
  316.      WNDCLASS wndclass ;
  317.  
  318.      if (!hPrevInstance) 
  319.           {
  320.           wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
  321.           wndclass.lpfnWndProc   = WndProc ;
  322.           wndclass.cbClsExtra    = 0 ;
  323.           wndclass.cbWndExtra    = 0 ;
  324.           wndclass.hInstance     = hInstance ;
  325.           wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
  326.           wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
  327.           wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
  328.           wndclass.lpszMenuName  = NULL ;
  329.           wndclass.lpszClassName = szAppName ;
  330.  
  331.           RegisterClass (&wndclass) ;
  332.           }
  333.  
  334.      hwnd = CreateWindow (szAppName, "Keyboard MIDI Player",
  335.                           WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
  336.               CW_USEDEFAULT, CW_USEDEFAULT,
  337.                           CW_USEDEFAULT, CW_USEDEFAULT,
  338.                           NULL, NULL, hInstance, NULL) ;
  339.  
  340.      ShowWindow (hwnd, nCmdShow) ;
  341.      UpdateWindow (hwnd); 
  342.  
  343.      while (GetMessage (&msg, NULL, 0, 0))
  344.           {
  345.           TranslateMessage (&msg) ;
  346.           DispatchMessage (&msg) ;
  347.           }
  348.      return msg.wParam ;
  349.      }
  350.  
  351.           // Create the program's menu (called from WndProc, WM_CREATE)
  352.           // ----------------------------------------------------------
  353.  
  354. HMENU CreateTheMenu (int iNumDevs)
  355.      {
  356.      char        szBuffer [32] ;
  357.      HMENU       hMenu, hMenuPopup, hMenuSubPopup ;
  358.      int         i, iFam, iIns ;
  359.      MIDIOUTCAPS moc ;
  360.  
  361.      hMenu = CreateMenu () ;
  362.  
  363.                // Create "On/Off" popup menu
  364.  
  365.      hMenuPopup = CreateMenu () ;
  366.  
  367.      AppendMenu (hMenuPopup, MF_STRING             , IDM_OPEN,  "&Open") ;
  368.      AppendMenu (hMenuPopup, MF_STRING | MF_CHECKED, IDM_CLOSE, "&Closed") ;
  369.  
  370.      AppendMenu (hMenu, MF_STRING | MF_POPUP, hMenuPopup, "&Status") ;
  371.  
  372.                // Create "Device" popup menu
  373.  
  374.      hMenuPopup = CreateMenu () ;
  375.  
  376.                          // Put MIDI Mapper on menu if it's installed
  377.  
  378.      if (!midiOutGetDevCaps (MIDIMAPPER, &moc, sizeof (moc)))
  379.           AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + MIDIMAPPER,
  380.                       moc.szPname) ;
  381.      else
  382.           iDevice = 0 ;
  383.  
  384.                          // Add the rest of the MIDI devices
  385.  
  386.      for (i = 0 ; i < iNumDevs ; i++)
  387.           {
  388.           midiOutGetDevCaps (i, &moc, sizeof (moc)) ;
  389.           AppendMenu (hMenuPopup, MF_STRING, IDM_DEVICE + i, moc.szPname) ;
  390.           }
  391.  
  392.      CheckMenuItem (hMenuPopup, 0, MF_BYPOSITION | MF_CHECKED) ;
  393.      AppendMenu (hMenu, MF_STRING | MF_POPUP, hMenuPopup, "&Device") ;
  394.  
  395.                // Create "Channel" popup menu
  396.  
  397.      hMenuPopup = CreateMenu () ;
  398.  
  399.      for (i = 0 ; i < 16 ; i++)
  400.           {
  401.           wsprintf (szBuffer, "%d", i + 1) ;
  402.           AppendMenu (hMenuPopup,
  403.                       MF_STRING | (i ? MF_UNCHECKED : MF_CHECKED),
  404.                       IDM_CHANNEL + i, szBuffer) ;
  405.           }
  406.  
  407.      AppendMenu (hMenu, MF_STRING | MF_POPUP, hMenuPopup, "&Channel") ;
  408.  
  409.                // Create "Voice" popup menu
  410.  
  411.      hMenuPopup = CreateMenu () ;
  412.  
  413.      for (iFam = 0 ; iFam < 16 ; iFam++)
  414.           {
  415.           hMenuSubPopup = CreateMenu () ;
  416.  
  417.           for (iIns = 0 ; iIns < 8 ; iIns++)
  418.                {
  419.                wsprintf (szBuffer, "&%d.\t%s", iIns + 1,
  420.                          (LPSTR) fam[iFam].inst[iIns].szInst) ;
  421.                AppendMenu (hMenuSubPopup,
  422.                            MF_STRING | (fam[iFam].inst[iIns].iVoice ?
  423.                                         MF_UNCHECKED : MF_CHECKED),
  424.                            fam[iFam].inst[iIns].iVoice + IDM_VOICE,
  425.                            szBuffer) ;
  426.                }
  427.  
  428.           wsprintf (szBuffer, "&%c.\t%s", 'A' + iFam,
  429.                     (LPSTR) fam[iFam].szFam) ;
  430.           AppendMenu (hMenuPopup, MF_STRING | MF_POPUP, hMenuSubPopup,
  431.                       szBuffer) ;
  432.           }
  433.  
  434.      AppendMenu (hMenu, MF_STRING | MF_POPUP, hMenuPopup, "&Voice") ;
  435.  
  436.      return hMenu ;
  437.      }
  438.  
  439.           // Routines for simplifying MIDI output
  440.           // ------------------------------------
  441.  
  442. DWORD MidiOutMessage (HMIDIOUT hMidi, int iStatus, int iChannel,
  443.                                       int iData1,  int iData2)
  444.      {
  445.      DWORD dwMessage ;
  446.  
  447.      dwMessage = iStatus | iChannel | (iData1 << 8) | ((long) iData2 << 16) ;
  448.  
  449.      return midiOutShortMsg (hMidi, dwMessage) ;
  450.      }
  451.  
  452. DWORD MidiNoteOff (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel)
  453.      {
  454.      return MidiOutMessage (hMidi, 0x080, iChannel, 12 * iOct + iNote, iVel) ;
  455.      }
  456.  
  457. DWORD MidiNoteOn (HMIDIOUT hMidi, int iChannel, int iOct, int iNote, int iVel)
  458.      {
  459.      return MidiOutMessage (hMidi, 0x090, iChannel, 12 * iOct + iNote, iVel) ;
  460.      }
  461.  
  462. DWORD MidiSetPatch (HMIDIOUT hMidi, int iChannel, int iVoice)
  463.      {
  464.      return MidiOutMessage (hMidi, 0x0C0, iChannel, iVoice, 0) ;
  465.      }
  466.  
  467. DWORD MidiPitchBend (HMIDIOUT hMidi, int iChannel, int iBend)
  468.      {
  469.      return MidiOutMessage (hMidi, 0x0E0, iChannel, iBend & 0x7F, iBend >> 7) ;
  470.      }
  471.  
  472.           // Draw a single key on window
  473.           // ---------------------------
  474.  
  475. VOID DrawKey (HDC hdc, int iScanCode, BOOL fInvert)
  476.      {
  477.      RECT rc ;
  478.  
  479.      rc.left   = 3 * cxCaps * key[iScanCode].xPos / 2 + xOffset ;
  480.      rc.top    = 3 * cyChar * key[iScanCode].yPos / 2 + yOffset ;
  481.      rc.right  = rc.left + 3 * cxCaps ;
  482.      rc.bottom = rc.top  + 3 * cyChar / 2 ;
  483.  
  484.      SetTextColor (hdc, fInvert ? 0x00FFFFFFul : 0x00000000ul) ;
  485.      SetBkColor   (hdc, fInvert ? 0x00000000ul : 0x00FFFFFFul) ;
  486.  
  487.      FillRect (hdc, &rc, GetStockObject (fInvert ? BLACK_BRUSH : WHITE_BRUSH)) ;
  488.  
  489.      DrawText (hdc, key[iScanCode].szKey, -1, &rc,
  490.                DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
  491.  
  492.      FrameRect (hdc, &rc, GetStockObject (BLACK_BRUSH)) ;
  493.      }
  494.  
  495.           // Process a Key Up or Key Down message
  496.           // ------------------------------------
  497.  
  498. VOID ProcessKey (HDC hdc, WORD message, LONG lParam)
  499.      {
  500.      int iScanCode, iOctave, iNote ;
  501.  
  502.      iScanCode = 0x0FF & HIWORD (lParam) ;
  503.  
  504.      if (iScanCode >= NUMSCANS)                       // No scan codes over 53
  505.           return ;
  506.  
  507.      if ((iOctave = key[iScanCode].iOctave) == -1)    // Non-music key
  508.           return ;
  509.  
  510.      if (GetKeyState (VK_SHIFT) < 0)
  511.           iOctave += 0x20000000 & lParam ? 2 : 1 ;
  512.  
  513.      if (GetKeyState (VK_CONTROL) < 0)
  514.           iOctave -= 0x20000000 & lParam ? 2 : 1 ;
  515.  
  516.      iNote = key[iScanCode].iNote ;
  517.  
  518.      if (message == WM_KEYUP)                           // For key up
  519.           {
  520.           MidiNoteOff (hMidiOut, iChannel, iOctave, iNote, 0) ;   // Note off
  521.           DrawKey (hdc, iScanCode, FALSE) ;
  522.           return ;
  523.           }
  524.  
  525.      if (0x40000000 & lParam)                          // ignore typematics
  526.           return ;
  527.  
  528.      MidiNoteOn (hMidiOut, iChannel, iOctave, iNote, iVelocity) ; // Note on
  529.      DrawKey (hdc, iScanCode, TRUE) ;                 // Draw the inverted key
  530.      }
  531.  
  532.           // Window Procedure
  533.           // ----------------
  534.  
  535. long FAR PASCAL _export WndProc (HWND hwnd, UINT message, UINT wParam,
  536.                                                           LONG lParam)
  537.      {
  538.      static BOOL bOpened = FALSE ;
  539.      char        szBuffer [16] ;
  540.      DWORD       dwExtent ;
  541.      HDC         hdc ;
  542.      HMENU       hMenu ;
  543.      MIDIOUTCAPS moc ;
  544.      PAINTSTRUCT ps ;
  545.      short       i, iNumDevs, iPitchBend, cxClient, cyClient ;
  546.  
  547.      switch (message)
  548.           {
  549.           case WM_CREATE:
  550.                          // Get size of capital letters in system font
  551.  
  552.                hdc = GetDC (hwnd) ;
  553.  
  554.                dwExtent = GetTextExtent (hdc, "M", 1) ;
  555.                cxCaps   = LOWORD (dwExtent) ;
  556.                cyChar   = HIWORD (dwExtent) ;
  557.  
  558.                ReleaseDC (hwnd, hdc) ;
  559.  
  560.                          // Initialize "Volume" scroll bar
  561.  
  562.                SetScrollRange (hwnd, SB_HORZ, 1, 127, FALSE) ;
  563.                SetScrollPos   (hwnd, SB_HORZ, iVelocity, TRUE) ;
  564.  
  565.                          // Initialize "Pitch Bend" scroll bar
  566.  
  567.                SetScrollRange (hwnd, SB_VERT, 0, 16383, FALSE) ;
  568.                SetScrollPos   (hwnd, SB_VERT, 8192, TRUE) ;
  569.  
  570.                          // Get number of MIDI output devices and set up menu
  571.  
  572.                if (0 == (iNumDevs = midiOutGetNumDevs ()))
  573.                     {
  574.                     MessageBeep (MB_ICONSTOP) ;
  575.                     MessageBox (hwnd, "No MIDI output devices",
  576.                                 szAppName, MB_OK | MB_ICONSTOP) ;
  577.                     DestroyWindow (hwnd) ;
  578.                     }
  579.                else
  580.                     {
  581.                     SetMenu (hwnd, CreateTheMenu (iNumDevs)) ;
  582.                     }
  583.  
  584.                return 0 ;
  585.  
  586.           case WM_SIZE:
  587.                cxClient = LOWORD (lParam) ;
  588.                cyClient = HIWORD (lParam) ;
  589.  
  590.                xOffset = (cxClient - 25 * 3 * cxCaps / 2) / 2 ;
  591.                yOffset = (cyClient - 11 * cyChar) / 2 + 5 * cyChar ;
  592.                return 0 ;
  593.  
  594.           case WM_COMMAND:
  595.                hMenu = GetMenu (hwnd) ;
  596.  
  597.                          // "Open" menu command
  598.  
  599.                if (wParam == IDM_OPEN && !bOpened)
  600.                     {
  601.                     if (midiOutOpen (&hMidiOut, iDevice, NULL, 0L, 0L))
  602.                          {
  603.                          MessageBeep (MB_ICONEXCLAMATION) ;
  604.                          MessageBox (hwnd, "Cannot open MIDI device",
  605.                                      szAppName, MB_OK | MB_ICONEXCLAMATION) ;
  606.                          }
  607.                     else
  608.                          {
  609.                          CheckMenuItem (hMenu, IDM_OPEN,  MF_CHECKED) ;
  610.                          CheckMenuItem (hMenu, IDM_CLOSE, MF_UNCHECKED) ;
  611.  
  612.                          MidiSetPatch (hMidiOut, iChannel, iVoice) ;
  613.                          bOpened = TRUE ;
  614.                          }
  615.                     }
  616.  
  617.                          // "Close" menu command
  618.  
  619.                else if (wParam == IDM_CLOSE && bOpened)
  620.                     {
  621.                     CheckMenuItem (hMenu, IDM_OPEN,  MF_UNCHECKED) ;
  622.                     CheckMenuItem (hMenu, IDM_CLOSE, MF_CHECKED) ;
  623.  
  624.                               // Turn all keys off and close device
  625.  
  626.                     for (i = 0 ; i < 16 ; i++)
  627.                          MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ;
  628.  
  629.                     midiOutClose (hMidiOut) ;
  630.                     bOpened = FALSE ;
  631.                     }
  632.  
  633.                          // Change MIDI "Device" menu command
  634.  
  635.                else if (wParam >= IDM_DEVICE - 1 && wParam < IDM_CHANNEL)
  636.                     {
  637.                     CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_UNCHECKED) ;
  638.                     iDevice = wParam - IDM_DEVICE ;
  639.                     CheckMenuItem (hMenu, IDM_DEVICE + iDevice, MF_CHECKED) ;
  640.  
  641.                               // Close and reopen MIDI device
  642.  
  643.                     if (bOpened)
  644.                          {
  645.                          SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ;
  646.                          SendMessage (hwnd, WM_COMMAND, IDM_OPEN,  0L) ;
  647.                          }
  648.                     }
  649.  
  650.                          // Change MIDI "Channel" menu command
  651.  
  652.                else if (wParam >= IDM_CHANNEL && wParam < IDM_VOICE)
  653.                     {
  654.                     CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_UNCHECKED);
  655.                     iChannel = wParam - IDM_CHANNEL ;
  656.                     CheckMenuItem (hMenu, IDM_CHANNEL + iChannel, MF_CHECKED) ;
  657.  
  658.                     if (bOpened)
  659.                          MidiSetPatch (hMidiOut, iChannel, iVoice) ;
  660.                     }
  661.  
  662.                          // Change MIDI "Voice" menu command
  663.  
  664.                else if (wParam >= IDM_VOICE)
  665.                     {
  666.                     CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_UNCHECKED) ;
  667.                     iVoice = wParam - IDM_VOICE ;
  668.                     CheckMenuItem (hMenu, IDM_VOICE + iVoice, MF_CHECKED) ;
  669.  
  670.                     if (bOpened)
  671.                          MidiSetPatch (hMidiOut, iChannel, iVoice) ;
  672.                     }
  673.  
  674.                InvalidateRect (hwnd, NULL, TRUE) ;
  675.                return 0 ;
  676.  
  677.                     // Process a Key Up or Key Down message
  678.  
  679.           case WM_KEYUP:
  680.           case WM_KEYDOWN:
  681.                hdc = GetDC (hwnd) ;
  682.  
  683.                if (bOpened)
  684.                     ProcessKey (hdc, message, lParam) ;
  685.  
  686.                ReleaseDC (hwnd, hdc) ;
  687.                return 0 ;
  688.  
  689.                     // For Escape, turn off all notes and repaint
  690.  
  691.           case WM_CHAR:
  692.                if (bOpened && wParam == 27)
  693.                     {
  694.                     for (i = 0 ; i < 16 ; i++)
  695.                          MidiOutMessage (hMidiOut, 0xB0, i, 123, 0) ;
  696.  
  697.                     InvalidateRect (hwnd, NULL, TRUE) ;
  698.                     }
  699.                return 0 ;
  700.  
  701.                     // Horizontal scroll: Velocity
  702.  
  703.           case WM_HSCROLL:
  704.                switch (wParam)
  705.                     {
  706.                     case SB_LINEUP:         iVelocity -= 1 ;  break ;
  707.                     case SB_LINEDOWN:       iVelocity += 1 ;  break ;
  708.                     case SB_PAGEUP:         iVelocity -= 8 ;  break ;
  709.                     case SB_PAGEDOWN:       iVelocity += 8 ;  break ;
  710.                     case SB_THUMBPOSITION:
  711.                          iVelocity = LOWORD (lParam) ;
  712.                          break ;
  713.  
  714.                     default:
  715.                          return 0 ;
  716.                     }
  717.  
  718.                iVelocity = max (1, min (iVelocity, 127)) ;
  719.                SetScrollPos (hwnd, SB_HORZ, iVelocity, TRUE) ;
  720.                return 0 ;
  721.  
  722.                     // Vertical scroll:  Pitch Bend
  723.  
  724.           case WM_VSCROLL:
  725.                switch (wParam)
  726.                     {
  727.                     case SB_THUMBTRACK:
  728.                          iPitchBend = 16383 - LOWORD (lParam) ;
  729.                          break ;
  730.  
  731.                     case SB_THUMBPOSITION:
  732.                          iPitchBend = 8191 ;
  733.                          break ;
  734.  
  735.                     default:
  736.                          return 0 ;
  737.                     }
  738.  
  739.                iPitchBend = max (0, min (iPitchBend, 16383)) ;
  740.                SetScrollPos (hwnd, SB_VERT, 16383 - iPitchBend, TRUE) ;
  741.  
  742.                if (bOpened)
  743.                     MidiPitchBend (hMidiOut, iChannel, iPitchBend) ;
  744.                return 0 ;
  745.  
  746.           case WM_PAINT:
  747.                hdc = BeginPaint (hwnd, &ps) ;
  748.  
  749.                for (i = 0 ; i < NUMSCANS ; i++)
  750.                     if (key[i].xPos != -1)
  751.                          DrawKey (hdc, i, FALSE) ;
  752.  
  753.                midiOutGetDevCaps (iDevice, &moc, sizeof (MIDIOUTCAPS)) ;
  754.                wsprintf (szBuffer, "Channel %d", iChannel + 1) ;
  755.  
  756.                TextOut (hdc, cxCaps, 1 * cyChar, bOpened ? "Open" : "Closed",
  757.                                                  bOpened ? 4 : 6) ;
  758.                TextOut (hdc, cxCaps, 2 * cyChar, moc.szPname,
  759.                                                  strlen (moc.szPname)) ;
  760.                TextOut (hdc, cxCaps, 3 * cyChar, (LPSTR) szBuffer,
  761.                                                  strlen (szBuffer)) ;
  762.                TextOut (hdc, cxCaps, 4 * cyChar,
  763.                         (LPSTR) fam[iVoice / 8].inst[iVoice % 8].szInst,
  764.                         strlen (fam[iVoice / 8].inst[iVoice % 8].szInst)) ;
  765.  
  766.                EndPaint (hwnd, &ps) ;
  767.                return 0 ;
  768.  
  769.           case WM_DESTROY :
  770.                SendMessage (hwnd, WM_COMMAND, IDM_CLOSE, 0L) ;
  771.  
  772.                PostQuitMessage (0) ;
  773.                return 0 ;
  774.           }
  775.      return DefWindowProc (hwnd, message, wParam, lParam) ;
  776.      }
  777.